/*
 * TagList.cpp
 *
 *  Created on: 19 juin 2011
 *      Author: Pierrick Caillon <pierrick@megami.fr>
 */

#include "TagList.h"
#include "TagByte.h"
#include "TagInt.h"
#include "TagFactory.h"

/* NAMEDTAG_H_ */
/* NAMEDTAG_H_ */
/* TAGBYTE_H_ */
/* NAMEDTAG_H_ */
/* ITAG_H_ */
namespace NamedBinaryTag
{
    TagList::TagList()
    :NamedTag(), listType(TAG_END)
    {
    }

    TagList::~TagList()
    {
        this->Clear();
    }

    TagTypes TagList::get_TagType() const
    {
        return TAG_LIST;
    }

    void TagList::Read(std::istream & input, bool ignoreName)
    {
        NamedTag::Read(input, ignoreName);
        TagByte type;
        type.Read(input, true);
        TagInt length;
        length.Read(input, true);
        std::vector<ITag*> items;
        size_t l = (size_t)(length.get_Value());
        for (size_t i = 0; i < l; i++)
	{
		ITag *element = TagFactory::CreateTag((TagTypes) type.get_Value());
		if (dynamic_cast<NamedTag*> (element) != NULL)
			dynamic_cast<NamedTag*> (element)->Read(input, true);
		else
			element->Read(input);
		items.push_back(element);
	}
        this->Clear();
        this->listType = (TagTypes)(type.get_Value());
        this->items.swap(items);
        for(std::vector<ITag*>::const_iterator it = items.begin();it != items.end();it++){
            TagFactory::DeleteTag(*it);
        }
    }

    void TagList::Write(std::ostream & output, bool ignoreName) const
    {
        NamedTag::Write(output, ignoreName);
        TagByte type;
        type.set_Value(this->listType);
        type.Write(output, true);
        TagInt length;
        length.set_Value(this->items.size());
        length.Write(output, true);
        for (std::vector<ITag*>::const_iterator it = this->items.begin(); it
			!= this->items.end(); it++)
		if (dynamic_cast<NamedTag*> (*it) != NULL)
			dynamic_cast<NamedTag*> (*it)->Write(output, true);
		else
			(*it)->Write(output);
    }

    TagTypes TagList::get_ListType() const
    {
        return this->listType;
    }

    void TagList::set_ListType(TagTypes type)
    {
        if (type != this->listType)
	{
		if (type == TAG_END)
			std::__throw_invalid_argument(
					__N("TagList::set_ListType: TagEnd type is not usable."));
		this->Clear();
		this->listType = type;
	}
    }

    bool TagList::Add(ITag *tag)
    {
        if (tag == NULL)
		std::__throw_invalid_argument(
				__N("TagList::set_Item: tag argument is NULL."));
        if (tag->get_TagType() != this->listType)
		std::__throw_invalid_argument(
				__N("TagList::set_Item: tag is not of the expected type."));
        if ((tag->root != NULL) && (tag->root != this))
		std::__throw_invalid_argument(
				__N("TagList::Add: Trying to add a Tag neither unbound nor bound to this container."));
        for (std::vector<ITag*>::iterator it = this->items.begin(); it
			!= this->items.end(); it++)
	{
		if (*it == tag)
		{
			std::__throw_invalid_argument(
					__N("TagList::Add: Trying to add again a Tag instance."));
		}
	}
        this->items.push_back(tag);
        return true;
    }

    bool TagList::Insert(ITag *tag, size_t index) throw ()
    {
        if (tag == NULL)
		std::__throw_invalid_argument(
				__N("TagList::set_Item: tag argument is NULL."));
        if (tag->get_TagType() != this->listType)
		std::__throw_invalid_argument(
				__N("TagList::set_Item: tag is not of the expected type."));
        if ((tag->root != NULL) && (tag->root != this))
		std::__throw_invalid_argument(
				__N("TagList::Insert: Trying to add a Tag neither unbound nor bound to this container."));
        for (std::vector<ITag*>::iterator it = this->items.begin(); it
			!= this->items.end(); it++)
	{
		if (*it == tag)
		{
			std::__throw_invalid_argument(
					__N("TagList::Insert: Trying to insert again a Tag instance."));
		}
	}
        this->items.insert(this->items.begin() + index, tag->clone());
        return true;
    }

    bool TagList::Remove(ITag *tag)
    {
        for(std::vector<ITag*>::iterator it = this->items.begin();it != this->items.end();it++)
            if(*it == tag){
                this->items.erase(it);
                return true;
            }

        return false;
    }

    bool TagList::Remove(size_t index) throw ()
    {
        if(index < this->items.size()){
            TagFactory::DeleteTag(*(this->items.begin() + index));
            this->items.erase(this->items.begin() + index);
            return true;
        }
        std::__throw_out_of_range(__N("TagList::Remove: Subscript out of range."));
    }

    ITag *TagList::operator [](size_t index) const throw ()
    {
        return this->get_Item(index);
    }

    size_t TagList::get_Count() const
    {
        return this->items.size();
    }

    ITag *TagList::get_Item(size_t index) const throw ()
    {
        if(index < this->items.size())
            return this->items[index];

        std::__throw_out_of_range(__N("TagList::get_Item: Subscript out of range."));
    }
    void TagList::set_Item(size_t index, ITag *tag) throw ()
    {
        if (tag == NULL)
		std::__throw_invalid_argument(
				__N("TagList::set_Item: tag argument is NULL."));
        if (tag->get_TagType() != this->listType)
		std::__throw_invalid_argument(
				__N("TagList::set_Item: tag is not of the expected type."));
        if(this->items[index] == tag)
            return;

        for (std::vector<ITag*>::iterator it = this->items.begin(); it
			!= this->items.end(); it++)
	{
		if (*it == tag)
		{
			std::__throw_invalid_argument(
					__N("TagList::set_Item: Trying to set a Tag with a Tag instance which is in the list."));
		}
	}
        if(index < this->items.size()){
            TagFactory::DeleteTag(this->items[index]);
            this->items[index] = tag;
        }
        std::__throw_out_of_range(__N("TagList::get_Item: Subscript out of range."));
    }

    void TagList::Clear()
    {
        for(std::vector<ITag*>::const_iterator it = this->items.begin();it != this->items.end();it++){
            TagFactory::DeleteTag(*it);
        }
        this->items.clear();
    }

    ITag *TagList::clone() const
    {
    	return this->clone(NULL);
    }

    ITag *TagList::clone(void *newRoot) const
    {
        TagList *copy = new TagList(newRoot);
        copy->listType = this->listType;
        for(size_t i = 0;i < this->items.size();i++)
            copy->Add(this->items[i]->clone(newRoot));

        return copy;
    }

}
